#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <cstring>
#include <zlib.h>

#include "includes/M.h"

const size_t CMD_POS = 1;
const size_t OUTPUT_POS = 2;
const size_t INPUT_BEG_POS = 3;
const size_t BLOCK_SIZE = 1024 * 1024;
const size_t BATCH_SIZE = 10 * 1024;
const size_t COMPRESS_LEVEL = 7;

struct HSRFileHeader {
    char magicNumber[3];
    char version;
    uint32_t fileInfoCount;

    HSRFileHeader() {
        std::memcpy(magicNumber, "HSR", 3);
        version = 1;
        fileInfoCount = 0;
    }

    bool isValidMagic() const {
        return std::memcmp(magicNumber, "HSR", 3) == 0;
    }

    bool isSupportedVersion() const {
        return version == 1;
    }
};

struct FileStruct {
    char fileName[24];
    uint64_t fileSize;
    uint32_t hash;
    FileStruct() : fileSize(0), hash(0) {
        std::memcpy(fileName, "NIL", 3);
    }
};

bool writeCustomFile(const std::string& filename, uint32_t fileInfoCount) {
    std::ofstream file(filename, std::ios::binary | std::ios::out);
    if (!file.is_open()) {
        std::cerr << "Unable to open file.(WRITE HSR)" << std::endl;
        return false;
    }
    HSRFileHeader header;
    header.fileInfoCount = fileInfoCount;  // 设置文件信息的数量
    file.write(reinterpret_cast<const char*>(&header), sizeof(header));
    if (!file) {
        std::cerr << "Error writing file header." << std::endl;
        return false;
    }
    std::cout << "File header written successfully." << std::endl;
    return true;
}

bool readCustomFile(const std::string& filename, uint32_t& fileInfoCount) {
    std::ifstream file(filename, std::ios::binary);
    if (!file.is_open()) {
        std::cerr << "Unable to open file." << std::endl;
        return false;
    }
    HSRFileHeader header;
    file.read(reinterpret_cast<char*>(&header), sizeof(header));
    if (file.gcount() != sizeof(header) || !header.isValidMagic()) {
        std::cerr << "Invalid magic number." << std::endl;
        return false;
    }
    if (!header.isSupportedVersion()) {
        std::cerr << "Unsupported file version." << std::endl;
        return false;
    }
    fileInfoCount = header.fileInfoCount;  // 获取文件信息的数量
    std::cout << "File read successfully." << std::endl;
    return true;
}

uint32_t getHash(std::ifstream& file, std::streamsize fileSize) {
    const uint32_t seed = 114514;
    uint32_t hash;
    std::vector<char> buffer(BLOCK_SIZE);
    MurmurHash3_x86_32(nullptr, 0, seed, &hash);

    std::streamsize remaining = fileSize;
    while (remaining > 0) {
        std::streamsize readSize = std::min(remaining, static_cast<std::streamsize>(BLOCK_SIZE));
        file.read(buffer.data(), readSize);
        if (!file) {
            std::cerr << "Error reading file in getHash." << std::endl;
            return 0;
        }
        uint32_t blockHash;
        MurmurHash3_x86_32(buffer.data(), readSize, seed, &blockHash);
        // 使用位运算组合哈希值
        hash = (hash << 5) ^ (hash >> 27) ^ blockHash;
        remaining -= readSize;
    }
    return hash;
}

std::streamsize getFileSize(std::ifstream& file) {
    file.seekg(0, std::ios::end);
    std::streamsize size = file.tellg();
    file.seekg(0, std::ios::beg);
    return size;
}

int processICommand(int argc, char* argv[]) {
    std::vector<FileStruct> infoList;
    infoList.reserve(argc - INPUT_BEG_POS);
    std::vector<char> buffer(BATCH_SIZE);
    for (int i = INPUT_BEG_POS; i < argc; i++) {
        std::cout << argv[i] << std::endl;
        FileStruct fileInfo;
        std::ifstream file(argv[i], std::ios::binary | std::ios::in);
        if (!file.is_open()) {
            std::cerr << "Can't find file: " << argv[i] << std::endl;
            return 1;
        }
        std::strncpy(fileInfo.fileName, argv[i], sizeof(fileInfo.fileName) - 1);
        fileInfo.fileName[sizeof(fileInfo.fileName) - 1] = '\0';
        fileInfo.fileSize = static_cast<uint64_t>(getFileSize(file));
        fileInfo.hash = getHash(file, fileInfo.fileSize);

        std::cout << "File size:" << fileInfo.fileSize << std::endl;
        infoList.push_back(fileInfo);
    }

    std::ifstream checkFile(argv[OUTPUT_POS], std::ios::binary);
    if (checkFile.is_open()) {
        std::cerr << "target file: " << argv[OUTPUT_POS] << " exists." << std::endl;
        checkFile.close();
        return 1;
    }

    if (!writeCustomFile(argv[OUTPUT_POS], static_cast<uint32_t>(infoList.size()))) {
        return 1;
    }
    std::ofstream outFile(argv[OUTPUT_POS], std::ios::binary | std::ios::app);
    if (!outFile.is_open()) {
        std::cerr << "Unable to open output file." << std::endl;
        return 1;
    }
    for (const FileStruct& fs : infoList) {
        outFile.write(reinterpret_cast<const char*>(&fs), sizeof(FileStruct));
        if (!outFile) {
            std::cerr << "Error writing file info." << std::endl;
            return 1;
        }
    }
    std::cout << "File info written successfully." << std::endl;

    for (const FileStruct& fs : infoList) {
        std::ifstream inFileforW(fs.fileName, std::ios::binary | std::ios::in);
        if (!inFileforW.is_open()) {
            std::cerr << "Error opening input file: " << fs.fileName << std::endl;
            return 1;
        }
        
        std::streamsize allBytes = fs.fileSize;

        while (allBytes > 0) {
            std::streamsize batchSize = std::min(allBytes, static_cast<std::streamsize>(BATCH_SIZE));
            inFileforW.read(buffer.data(), batchSize);
            outFile.write(buffer.data(), batchSize);
            allBytes -= batchSize;
        }

        inFileforW.close();
        std::cout << fs.fileName << " written." << std::endl;
    }
    std::cout << "Done." << std::endl;
    return 0;
}

int processXCommand(int argc, char* argv[]) {
    std::ifstream checkFile(argv[OUTPUT_POS], std::ios::binary);
    if (!checkFile.is_open()) {
        std::cerr << "target file: " << argv[OUTPUT_POS] << " does not exist." << std::endl;
        return 1;
    }
    checkFile.close();

    uint32_t fileInfoCount;
    if (!readCustomFile(argv[OUTPUT_POS], fileInfoCount))
        return 1;

    FileStruct readStruct;
    std::vector<FileStruct> fileList;
    std::ifstream inFile(argv[OUTPUT_POS], std::ios::binary | std::ios::in);
    if (!inFile.is_open()) {
        std::cerr << "Unable to open input file." << std::endl;
        return 1;
    }

    // 跳过文件头，读取文件信息
    inFile.seekg(sizeof(HSRFileHeader), std::ios::beg);
    for (uint32_t i = 0; i < fileInfoCount; ++i) {
        if (inFile.read(reinterpret_cast<char*>(&readStruct), sizeof(FileStruct))) {
            fileList.push_back(readStruct);
            std::ifstream existCheck(readStruct.fileName, std::ios::binary);
            if (existCheck.is_open()) {
                std::cerr << "File: " << readStruct.fileName << " exists." << std::endl;
                existCheck.close();
                return 1;
            }
        }
        else {
            std::cerr << "Error reading file info." << std::endl;
            return 1;
        }
    }
    for (const FileStruct& fs : fileList) {
        std::cout << fs.fileName << std::endl;
        std::cout << fs.fileSize << std::endl;
    }

    // 定位到文件内容的起始位置
    inFile.seekg(sizeof(HSRFileHeader) + fileList.size() * sizeof(FileStruct), std::ios::beg);

    for (const FileStruct& fs : fileList) {
        std::cout << fs.fileName << "\tsize: " << fs.fileSize << std::endl;
        std::ofstream outFile(fs.fileName, std::ios::binary | std::ios::out);
        if (!outFile.is_open()) {
            std::cerr << "Error opening output file: " << fs.fileName << std::endl;
            return 1;
        }
        std::streamsize allBytes = fs.fileSize;
        std::vector<char> buffer(BATCH_SIZE);

        while (allBytes > 0) {
            // 根据剩余字节数计算批量大小
            std::streamsize batchSize = std::min(allBytes, static_cast<std::streamsize>(BATCH_SIZE));
            inFile.read(buffer.data(), batchSize);
            if (!inFile) {
                std::cerr << "Wrong in read " << fs.fileName << std::endl;
                return 1;
            }
            outFile.write(buffer.data(), batchSize);
            if (!outFile) {
                std::cerr << "Error writing to output file: " << fs.fileName << std::endl;
                return 1;
            }
            allBytes -= batchSize;
        }
    }
    std::cout << "Done." << std::endl;
    return 0;
}

int processTCommand(int argc, char* argv[]) {
    // 检查参数数量和输出文件参数是否有效
    if (argc <= OUTPUT_POS || argv[OUTPUT_POS] == nullptr) {
        std::cerr << "Invalid output file argument." << std::endl;
        return 1;
    }

    uint32_t fileInfoCount;
    // 读取自定义文件，获取文件信息数量
    if (!readCustomFile(argv[OUTPUT_POS], fileInfoCount)) {
        return 1;
    }

    FileStruct readStruct;
    std::vector<FileStruct> fileList;
    // 以二进制只读模式打开输出文件
    std::ifstream inFile(argv[OUTPUT_POS], std::ios::binary | std::ios::in);
    if (!inFile.is_open()) {
        std::cerr << "Unable to open input file: " << argv[OUTPUT_POS] << std::endl;
        return 1;
    }

    // 跳过文件头，开始读取文件信息
    inFile.seekg(sizeof(HSRFileHeader), std::ios::beg);
    for (uint32_t i = 0; i < fileInfoCount; ++i) {
        if (inFile.read(reinterpret_cast<char*>(&readStruct), sizeof(FileStruct))) {
            fileList.push_back(readStruct);
        } else {
            std::cerr << "Error reading file info at index " << i << " from " << argv[OUTPUT_POS] << std::endl;
            return 1;
        }
    }

    // 定位到文件内容的起始位置
    inFile.seekg(sizeof(HSRFileHeader) + fileList.size() * sizeof(FileStruct), std::ios::beg);

    for (const FileStruct& fs : fileList) {
        std::string fileNameStr(fs.fileName);
        std::cout << "Verifying file: " << fileNameStr << std::endl;

        // 以二进制只读模式打开要验证的文件
        std::ifstream fileToVerify(fileNameStr, std::ios::binary | std::ios::in);
        if (!fileToVerify.is_open()) {
            std::cerr << "Error opening file to verify: " << fileNameStr << std::endl;
            return 1;
        }

        // 计算文件的哈希值
        uint32_t calculatedHash = getHash(fileToVerify, fs.fileSize);
        fileToVerify.close();

        // 比较计算得到的哈希值和文件中记录的哈希值
        if (calculatedHash != fs.hash) {
            std::cerr << "Hash mismatch for file: " << fileNameStr << std::endl;
            return 1;
        } else {
            std::cout << "Hash verification passed for file: " << fileNameStr << std::endl;
        }
    }

    std::cout << "All files verified successfully." << std::endl;
    return 0;
}

int main(int argc, char* argv[]) {
    std::cout << "hi." << std::endl;

    if (argc < OUTPUT_POS + 1) {
        std::cout << "Too few parameters." << std::endl;
        return 1;
    }

    if (std::strcmp("i", argv[CMD_POS]) == 0) {
        return processICommand(argc, argv);
    }
    else if (std::strcmp("x", argv[CMD_POS]) == 0) {
        return processXCommand(argc, argv);
    }
    else if (std::strcmp("ic",argv[CMD_POS]) == 0) {
        //TODO
    }
    else if (std::strcmp("xc",argv[CMD_POS]) == 0) {
        if (processXCommand(argc, argv) == 0) {

        }
    }
    else if (std::strcmp("t",argv[CMD_POS]) == 0) {
        processTCommand(argc, argv);
    }
    else {
        std::cout << "Bad parameter: " << argv[CMD_POS] << std::endl;
        return 1;
    }
}